#ifndef _PTR_TRAITS_H_

#include <marusdefs.h>

#include <memory>
#define _TR1    _STD tr1::

namespace ATL
{
    class CComObjectRootBase;
}

namespace marus
{
struct  _Reinterpret_tag {};
struct  _Cstyle_tag {};

class  refcounted_base;

template <typename T>
struct  _is_refcounted
{
    enum {value = __is_base_of(refcounted_base, T) };
};

template <typename T>
struct  _is_com_type
{
    enum {value = __is_base_of(IUnknown, T)};
};

template <typename T>
struct  _is_atlcom_type
{
    //enum {value = 0 };
    enum {value = __is_base_of(::ATL::CComObjectRootBase, T) };
};

template<typename T>
struct  _dispose_delete
{
    static  T*&  dispose(T*& ptr_)
    {
        if (ptr_ && !__is_on_stack(ptr_))
            delete ptr_;
        return ptr_ = NULL;
    }
};

template <typename T> 
struct   _dispose_delete_array
{
    static  T*&    dispose(T*& ptr_)
    {
        if (ptr_ && !__is_on_stack(ptr_))
            delete[] p;
        return ptr_ = NULL;
    }
};

template<typename T>
struct  _dispose_free
{
    static  T*& dispose(T*& ptr_)
    {
        if (ptr_ && !__is_on_stack(ptr_))
        {
            ::free(ptr_);
        }
        return ptr_ = NULL;
    }
};

template<typename T>
struct  _dont_dispose
{
public:
    static  T*& dispose(T*& ptr_)
    {
        return ptr_ = NULL;
    }
};

template <typename _Ty>
struct  native_pointer_cast
{
    typedef typename _Ty*    _Type;
};

template <typename _Ty>
struct const_caster : public native_pointer_cast<_Ty>
{
    static inline _Type cast(const _Ty* what_)
    {
        return const_cast<_Type>(what_);
    }
};

template <typename _Ty>
struct reinterpret_caster : public native_pointer_cast<_Ty>
{
    template<typename _Other>
    static inline _Type cast(_Other* what_)
    {
        return reinterpret_cast<_Type>(what_);
    }
};

template <typename _Ty>
struct static_caster : public native_pointer_cast<_Ty>
{
    template<typename _Other>
    static inline _Type cast(_Other* what_)
    {
        return static_cast<_Type>(what_);
    }
};

template <typename _Ty>
struct dynamic_caster : public native_pointer_cast<_Ty>
{
    template<typename _Other>
    static inline _Type cast(_Other* what_)
    {
        return dynamic_cast<_Type>(what_);
    }
};

template <typename _Ty>
struct cstyle_caster : public native_pointer_cast<_Ty>
{
    template<typename _Other>
    static inline _Type cast(_Other* what_)
    {
        return (_Type)(what_);
    }
};

template <typename _Ty>
class   _com_ptr 
{
public:
    typedef typename _Ty    _InterfaceType;
    typedef _com_ptr<_Ty>   _Myt;
    static inline REFIID  GetIID() throw()
    {
        static REFIID riid(__uuidof(_Ty));
        return riid;
    }

protected:
    _InterfaceType*    _p;

    INLINE void FASTCALL _AddRef() throw()
    {
        if (_p != NULL) 
            _p->AddRef();
    }

    INLINE void FASTCALL _Release() throw()
    {
        if (_p != NULL) 
            _p->Release();
    }

    void FASTCALL Attach(_InterfaceType* p_, bool addref_ = false) throw()
    {
        _Release();
        _p = p_;

        if (addref_) 
            _AddRef();
    }

    INLINE _InterfaceType* Detach() throw()
    {
        _InterfaceType* const ret = _p;
        _p = NULL;
        return ret;
    }


    template<typename _Other> 
    HRESULT FASTCALL _QI(_Other* p_) throw()
    {
        HRESULT hr = E_NOINTERFACE;

        // Can't QI NULL
        //
        if (p_ != NULL) 
        {
            // Query for this interface
            //
            _InterfaceType* p;
            hr = p_->QueryInterface(GetIID(), reinterpret_cast<void**>(&p));

            // Save the interface without AddRef()ing.
            //
            Attach(SUCCEEDED(hr)? p: NULL);
        }
        else 
            _Release();

        return hr;
    }

    _com_ptr()
        : _p(NULL)
    {}

public:

    _com_ptr(const _com_ptr& ref_) throw()
        : _p(ref_)
    {
        _AddRef();
    }

    template <typename _Other>
    _com_ptr(_Other* p_) throw()
        : _p(NULL)
    {
        _QI(p_);
    }

    template<> 
    _com_ptr(_InterfaceType* p_) throw()
        : _p(p_)
    { 
        _AddRef(); 
    }

    _com_ptr(int null) throw()
        : _p(NULL)
    {
        _ASSERT(null == 0);
    }

    ~_com_ptr() throw()
    {
        _Release();
    }

    operator _InterfaceType*() const throw()
    {
        return _p;
    }

    _InterfaceType* operator ->() const
    {
        if (!_p) 
            ::Inl::_Xnull_ptr();

        return _p;
    }
    IMPLEMENT_SAFE_BOOL_OPERATOR(_p != 0)
    //_TYPEDEF_BOOL_TYPE;
    //INLINE _OPERATOR_BOOL //operator _STD _Bool_type
    //   () const 
    //   throw()
    //{	// test if object owns no resource
    //    return (_p != 0 ? _CONVERTIBLE_TO_TRUE : 0);
    //}

    operator bool() const throw()
    { 
        return operator _Bool_type(); 
    }
};

template <typename _Ty>
struct com_caster
{

    typedef _com_ptr<_Ty>    _Type;

    template <typename _Other>
    static inline _Type cast(_Other* what_)
    {
        return (_Type(what_));
    }
};

template <typename _Ty>
struct   cast_result
{
    typedef typename 
    _IF <
        _is_com_type<_Ty>::value
        , typename com_caster<_Ty>::_Type
        , typename native_pointer_cast<_Ty>::_Type
    > :: type    _Type;
};

template <typename _Ty0, typename _Ty1>
struct   casting
{
    typedef typename 
    _IF <
        ( __is_class(_Ty0) && __is_class(_Ty1))
        , typename _IF <
            ( (_is_com_type<_Ty0>::value && (_is_com_type<_Ty1>::value || _is_atlcom_type<_Ty1>::value) )) //&& !(__is_base_of(_Ty0, _Ty1) && __is_base_of(_Ty1, _Ty0)))
            , com_caster<_Ty0>
            , typename _IF <
                (__is_base_of(_Ty0, _Ty1) || __is_base_of(_Ty1, _Ty0))
                , static_caster<_Ty0>
                , dynamic_caster<_Ty0>
            > :: type
        > ::type
        , reinterpret_caster<_Ty0>
    > :: type    value;

    typedef typename value::_Type _Type;
    static inline _Type cast(_Ty1* what_)
    {
        return value::cast(what_);
    }
};

template <typename T>
struct ptr_traits
{
    typedef typename T  _Type;
    typedef typename cast_result<_Type>::_Type    casted_pointer;
    typedef typename _TR1 add_reference<T>::type  _RefType;
    typedef _Type* pointer;
    typedef const _Type* const_pointer;
    typedef pointer& reference;
    typedef const_pointer& const_reference;

    template<typename _Other>
    static
    casted_pointer cast(const _Other* what_)
    {
        return best_cast<_Type>(const_cast<_Other*>(what_));
    }

    template<typename _Other>
    static
    const_reference cast_ref(const _Other*& what_)
    {
#ifdef _DEBUG
        cast(what_);
#endif
        return (const_reference)what_;
    }

    template<typename _Other>
    static
    reference cast_ref(_Other*& what_)
    {
#ifdef _DEBUG
        cast(what_);
#endif
        return (reference)what_;
    }

};

template <typename T>
struct   policies;

template <typename T, class disposer = _dont_dispose<T> >
struct policy_base : public ptr_traits<T>
{
    typedef typename ptr_traits<T>::_Type   _Type;
    typedef disposer    _Disposer;
    template<typename _Other>
    static
    casted_pointer    acquire(const _Other* what_)
    {
        return cast(what_);
    }

    static
    reference    release(reference ref_)
    {
        return _Disposer::dispose(ref_);
    }

    template<typename _Other>
    static
    casted_pointer    detach(_Other*& ref_)
    {
        casted_pointer ret = cast(ref_);
        ref_ = NULL;
        return ret;
    }

    template<typename _Other>
    static
    reference    copy(reference left_, _Other*& right_)
    {
        if ((const void*)left_ != (const void*)right_)
            release(left_) = acquire(right_);
        return left_;
    }
};

template <typename T, typename dispose_policy = _dont_dispose<T> >
struct default_policy :
    public policy_base<T, dispose_policy>
{

};

template <typename T>
struct auto_policy
    : public policy_base<T, typename policies<T>::disposer >
{
    template<typename _Other>
    static
    reference    copy(reference left_, _Other*& right_)
    {
        if ((const void*)left_ != (const void*)right_)
            release(left_) = detach(right_);
        return left_;
    }
};

template <typename T>
struct refcounted_policy
    : public policy_base<T>
{
    template<typename _Other>
    static
    casted_pointer    acquire(const _Other* what_)
    {
        casted_pointer ret = cast(what_);
        if (ret)
            ret->AddRef();
        return ret;
    }
    static
    reference    release(reference ref_)
    {
        if (ref_)
            ref_->Release();
        return ref_ = NULL;
    }

    template<typename _Other>
    static
    reference    copy(reference left_, _Other*& right_)
    {
        if ((const void*)left_ != (const void*)right_)
            release(left_) = acquire(right_);
        return left_;
    }
};

template <typename _Ty>
struct   policies
{
    typedef typename 
        _IF< 
            __is_class(_Ty)
            , _dispose_delete<_Ty>
            , _dispose_free<_Ty> 
            >::type   disposer;

    typedef typename 
        _IF< 
            _is_com_type<_Ty>::value
            , com_caster<_Ty>
            , dynamic_caster<_Ty>
            >::type  caster;

    typedef typename 
        _IF< 
            _is_refcounted<_Ty>::value || _is_com_type<_Ty>::value || _is_atlcom_type<_Ty>::value
            , refcounted_policy<_Ty>
            , default_policy<_Ty, typename policies<_Ty>::disposer > 
            >::type  life_span;

};

template <typename _Ty0, typename _Ty1>
static inline typename casting<_Ty0, _Ty1>::_Type  best_cast(_Ty1* what_)
{
    return casting<_Ty0, _Ty1>::value::cast(what_);
}

} // end namespace marus

#endif // #ifndef _PTR_TRAITS_H_
